module.exports = Point;

function Point(x, y, z, a) {
  this.x = x;
  this.y = y;
  this.z = z;
  this.a = a;
};

Point.prototype = {
  clone: function () {
    return new Point(this.x, this.y);
  },

  round: function () {
    return new Point(Math.round(this.x), Math.round(this.y));
  },

  each: function (f) {
    return new Point(f(this.x), f(this.y));
  },

  /**
   * 检查两个点是否相等。必须精确比较x和y值，才能返回true。
   * @name equal
   * @methodOf Point#
   *
   * @param {Point} other 要比较相等的另一个点。
   * @returns 如果此点等于另一个点，则返回true；否则返回false。
   * @type Boolean
   */
  equal: function (other) {
    return this.x === other.x && this.y === other.y;
  },

  /**
   * 将另一个点添加到此点，并返回新的点。
   * @name add
   * @methodOf Point#
   *
   * @param {Point} other 要添加到此点的点。
   * @returns 两点之和的新点。
   * @type Point
   */
  add: function (other) {
    return new Point(this.x + other.x, this.y + other.y);
  },

  /**
   * 从此点减去另一个点，并返回新的点。
   * @name sub
   * @methodOf Point#
   *
   * @param {Point} other 要从此点减去的点。
   * @returns 两点之差的新点。
   * @type Point
   */
  sub: function (other) {
    return new Point(this.x - other.x, this.y - other.y);
  },

  /**
   * 将此点乘以标量值，并返回新的点。
   * @name scale
   * @methodOf Point#
   *
   * @param {Point} scalar 要缩放此点的值。
   * @returns 乘以标量值后的新点。
   * @type Point
   */
  scale: function (scalar) {
    return new Point(this.x * scalar, this.y * scalar);
  },

  /**
   * 返回此点到原点的距离。如果将此点视为向量，则该距离为其大小。
   * @name magnitude
   * @methodOf Point#
   *
   * @returns 此点到原点的距离。
   * @type Number
   */
  magnitude: function (/* newMagnitude */) {
    if (arguments[0] === undefined)
      return Math.sqrt(this.x * this.x + this.y * this.y);

    return this.toUnit().multiply(arguments[0]);
  },

  multiply: function (d) {
    return new Point(this.x * d, this.y * d);
  },

  normalize: function () {
    return this.multiply(1 / this.magnitude());
  },

  set: function (x, y) {
    this.x = x;
    this.y = y;
  },

  dot: function (other) {
    return this.x * other.x + this.y * other.y;
  },

  translate: function (x, y) {
    return new Point(this.x + x, this.y + y);
  },

  rotate: function (a) {
    // 返回一个通过将该向量旋转a弧度的副本的新向量
    return new Vector(this.x * Math.cos(a) - this.y * Math.sin(a),
      this.x * Math.sin(a) + this.y * Math.cos(a));
  },

  angleTo: function (other) {
    return Math.acos(this.dot(other) / (Math.abs(this.magnitude()) * Math.abs(other.magnitude())));
  },

  toUnit: function () {
    return this.multiply(1 / this.magnitude());
  }
};

/**
 * @param {Point} p1
 * @param {Point} p2
 * @returns 两点之间的欧几里德距离。
 */
Point.distance = function (p1, p2) {
  return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
};

/**
 * 如果有两个人，一个站在点p1，另一个站在点p2，则此方法将返回站在p1的人需要面向p2的方向。
 * @param {Point} p1 起始点。
 * @param {Point} p2 结束点。
 * @returns 从p1到p2的方向（弧度）。
 */
Point.direction = function (p1, p2) {
  return Math.atan2(
    p2.y - p1.y,
    p2.x - p1.x
  );
};
